package com.dhp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dhp.Common.ErrorCode;
import com.dhp.Exception.exception;
import com.dhp.domain.User;
import com.dhp.domain.request.UserRegisterRequest;
import com.dhp.service.UserService;
import com.dhp.mapper.UserMapper;
import com.dhp.utils.AlgorithmUtil;
import com.dhp.utils.CookieUtil;
import com.dhp.utils.RedisUtil;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import javafx.util.Pair;
import lombok.extern.slf4j.Slf4j;
import nonapi.io.github.classgraph.json.JSONUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.dhp.Constant.UserConstant.DEFAULT_USER_ROLE;
import static com.dhp.Constant.UserConstant.RECOMMEND_USERS;

/**
* @author Dhp
* @description 针对表【user(用户)】的数据库操作Service实现
* @createDate 2024-02-16 14:01:21
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

    @Autowired
    private UserMapper userMapper;

    private static final String salt = "DHP";

    @Autowired
    private RedisUtil redisUtils;

    @Override
    public User userLogin(String userAccount, String userPassword, HttpServletRequest request , HttpServletResponse response) {
        //1.对传入的参数进行合法的校验
        //验证传入参数是否为空
        if (StringUtils.isAllBlank(userPassword , userAccount)){
            log.info("参数为空");
            throw new exception(ErrorCode.PARAMS_EMPTY);
        }

        //校验账户，不小于4位
        if (userAccount.length() < 4 || userPassword.length() < 8){
            log.info("账户或密码长度过短");
            throw new exception(ErrorCode.PARAMS_ERROR , "登录失败");
        }

        //校验账户的特殊字符
        Pattern accountValid = Pattern.compile("^[/|\\\\]*$");
        Matcher matcher = accountValid.matcher(userAccount);
        if (matcher.matches()){
            log.info("账户含有特殊字符");
            throw new exception(ErrorCode.PARAMS_ERROR , "账户含有特殊字符");
        }

        //2.校验用户名和密码是否输入正确(密码与数据库中加密的密码对比)
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        String encryptPassword = DigestUtils.md5DigestAsHex((userPassword + salt).getBytes());
        queryWrapper.eq(User::getUserAccount , userAccount).eq(User::getPassword , encryptPassword);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null){
            log.info("账户或者密码不正确");
            throw new exception(ErrorCode.PARAMS_ERROR , "账户或者密码不正确");
        }

        //3.user数据脱敏
        User safetyUser = getSafetyUser(user);

        //4.记录登录态 返回cookie，并将sessionid和用户对象存入redis
        //request.getSession().setAttribute(USR_LOGIN , safetyUser);
        String sessionId = request.getSession().getId();
        CookieUtil.setLoginToken(sessionId , response);
        redisUtils.set(sessionId , new Gson().toJson(safetyUser) , 1L , TimeUnit.HOURS);

        return safetyUser;
    }

    @Override
    public List<User> searchUserByTags(List<String> tagNameList , String searchText) {
        //1. 查询数据库
        List<User> users = userMapper.selectList(null);
        if (CollectionUtils.isEmpty(tagNameList) && StringUtils.isEmpty(searchText)) {
            return users;
        }
        //2. 进行过滤(内存过滤)
        Gson gson = new Gson();
        return users.stream().filter(user -> {
            String tagStr = user.getTags();
            Set<String> tagList = gson.fromJson(tagStr, new TypeToken<Set<String>>(){}.getType());
            //tagNameList不为空
            if (!CollectionUtils.isEmpty(tagNameList) && StringUtils.isEmpty(searchText)) {
                for (String s : tagNameList) {
                    if (!tagList.contains(s)) {
                        return false;
                    }
                }
            }else if(CollectionUtils.isEmpty(tagNameList) && !StringUtils.isEmpty(searchText)){
                if (!tagList.contains(searchText)){
                    return false;
                }
            } else {
                    if (!tagList.contains(searchText))
                        return false;
                    for (String s : tagNameList) {
                        if (!tagList.contains(s)) {
                            return false;
                        }
                    }
            }
            return true;
        }).map(this::getSafetyUser).collect(Collectors.toList());
    }


    @Override
    public User getCurrentUser(HttpServletRequest request) {
        //获取cookie中的sessionId,通过sessionId去redis中获取用户信息
        //User currentUser = (User) request.getSession().getAttribute(USR_LOGIN);
        String value = CookieUtil.readLoginToken(request);
        Gson gson = new Gson();
        User currentUser = gson.fromJson((String) redisUtils.get(value) , User.class);

        //2. 判断
        if (currentUser == null){
            log.info("当前未登录");
            throw new exception(ErrorCode.NOT_LOGIN , "当前用户未登录");
        }

        //3. 确保用户信息与数据库一致
        User user = userMapper.selectById(currentUser.getId());
        User safetyUser = getSafetyUser(user);
        return safetyUser;
    }

    @Override
    public Integer updateUser(User updatedUser) {
        if (BeanUtil.isEmpty(updatedUser)){
            throw new exception(ErrorCode.PARAMS_EMPTY , "请求参数为空");
        }
        int res = userMapper.updateById(updatedUser);
        return res;
    }

    @Override
    public Page<User> getRecommendUsers(int pageNum, int pageSize , HttpServletRequest request) {
        if (pageNum <= 0 || pageSize <= 0){
            throw new exception(ErrorCode.PARAMS_ERROR , "参数错误");
        }

        //校验redis中是否有缓存
        Gson gson = new Gson();
        User currentUser = getCurrentUser(request);
        Page<User> page = new Page<>(pageNum , pageSize);
        List<User> userList = gson.fromJson((String) redisUtils.get(RECOMMEND_USERS + currentUser.getId()), new TypeToken<List<User>>(){}.getType());

        //没有查询数据库 存入缓存
        if (userList == null){
            Page<User> userPage = userMapper.selectPage(new Page<>(pageNum, pageSize), null);
            String jsonStr = gson.toJson(userPage.getRecords());
            //存入redis
            redisUtils.set(RECOMMEND_USERS + currentUser.getId(), jsonStr, 2L , TimeUnit.DAYS);
            return userPage;
        };

        //有则返回
        page.setRecords(userList);
        return page;
    }


    public User getSafetyUser(User Unsafeuser){
        User user = new User();
        user.setId(Unsafeuser.getId());
        user.setUserName(Unsafeuser.getUserName());
        user.setUserAccount(Unsafeuser.getUserAccount());
        user.setAvatarUrl(Unsafeuser.getAvatarUrl());
        user.setGender(Unsafeuser.getGender());
        user.setPassword("");
        user.setPhone(Unsafeuser.getPhone());
        user.setEmail(Unsafeuser.getEmail());
        user.setStatus(Unsafeuser.getStatus());
        user.setGmtCreate(Unsafeuser.getGmtCreate());
        user.setGmtModified(Unsafeuser.getGmtModified());
        user.setIsDeleted(null);
        user.setUserRole(Unsafeuser.getUserRole());
        user.setProfile(Unsafeuser.getProfile());
        user.setPlanetCode(Unsafeuser.getPlanetCode());
        user.setTags(Unsafeuser.getTags());
        return user;
    }

    @Override
   public Boolean CheckAuthority(HttpServletRequest request){
        String value = CookieUtil.readLoginToken(request);
        User currentUser = new Gson().fromJson((String) redisUtils.get(value) , User.class);
        if (currentUser == null || currentUser.getUserRole() == DEFAULT_USER_ROLE){
            log.info("用户未登录或者权限不足");
            return false;
        }
        return true;
    }

    @Override
    public List<User> getMatchUsers(Integer num , HttpServletRequest request) {
        //判断参数是否为空
        if (num == null || num <= 0) {
            throw new exception(ErrorCode.PARAMS_ERROR);
        }
        //判断用户是否登录
        User currentUser = this.getCurrentUser(request);
        if (currentUser == null){
            throw new exception(ErrorCode.NOT_LOGIN);
        }
        Long userId = currentUser.getId();
        //查询数据库所有用户（只查询必要字段）
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id" , "tags");
        List<User> list = this.list(queryWrapper);
        //过滤不符合要求的信息，获取list中的tags标签
        Gson gson = new Gson();
        List<String> currentUserTags = gson.fromJson(currentUser.getTags(), new TypeToken<List<String>>() {
        }.getType());
        //yserId到tags标签的一个映射
        List<Pair<Long, Set<String>>> userIdTags = list.stream().filter(user -> {
            if (StringUtils.isEmpty(user.getTags()))
                return false;
            return true;
        }).map(user -> {
            Set<String> tagList = gson.fromJson(user.getTags(), new TypeToken<Set<String>>() {
            }.getType());
            Pair<Long, Set<String>> idTags = new Pair<>(user.getId(), tagList);
            return idTags;
        }).collect(Collectors.toList());
        //计算出相似度高的用户 得到一个id到距离分数的映射
        SortedMap<Integer , Long> idDisMap = new TreeMap<>();
        for (Pair<Long, Set<String>> userIdTag : userIdTags) {
            if (userIdTag.getKey().equals(userId)) {
                continue;
            }
            List<String> temp = new ArrayList<>();
            temp.addAll(userIdTag.getValue());
            int distance = AlgorithmUtil.minDistance(currentUserTags, temp);
            idDisMap.put(distance , userIdTag.getKey());
        }

        //获取map的value查询数据库用户完整信息
        List<Long> values = new ArrayList<>();
        Collection<Long> values1 = idDisMap.values();
        values.addAll(values1);
        List<Long> originalIdList = values.stream().limit(num).collect(Collectors.toList());
        //查询前num条数据
        List<User> unHandledUserList = this.listByIds(originalIdList);
        //按照原来的顺序对查到的数据进行排序
        Map<Long, List<User>> userMap = unHandledUserList.stream().collect(Collectors.groupingBy(User::getId));
        List<User> finalUserList = new ArrayList<>();
        for (Long aLong : originalIdList) {
            finalUserList.add(getSafetyUser(userMap.get(aLong).get(0)));
        }
        //返回结果
        return finalUserList;
    }

    @Override
    public Long userRegister(UserRegisterRequest registerRequest , HttpServletRequest request){

        String userAccount = registerRequest.getUserAccount();
        String userPassword = registerRequest.getPassword();
        String checkPassword = registerRequest.getCheckedPassword();
        String planetCode = registerRequest.getPlanetCode();
        //1.验证传入参数是否为空
        if (StringUtils.isAllBlank(userPassword , userAccount , checkPassword , planetCode)){
            log.info("参数为空");
            throw new exception(ErrorCode.PARAMS_EMPTY);
        }

        //2.校验账户，不小于4位
        if (userAccount.length() < 4 || userPassword.length() < 8){
            log.info("账户小于4位");
            throw new exception(ErrorCode.PARAMS_ERROR , "账户长度过短");
        }

        //3.校验账户的特殊字符
        Pattern accountValid = Pattern.compile("^[/|\\\\]*$");
        Matcher matcher = accountValid.matcher(userAccount);
        if (matcher.matches()){
            log.info("账户含有特殊字符");
            throw new exception(ErrorCode.PARAMS_ERROR , "账户含有特殊字符");
        }

        //4.校验初始密码和二次密码相同
        if (!userPassword.equals(checkPassword)){
            log.info("两次输入密码不一致");
            throw new exception(ErrorCode.PARAMS_ERROR , "两次输入密码不一致");
        }


        //5.账户不能重复
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserAccount , userAccount);
        Long result = userMapper.selectCount(queryWrapper);
        if (result != 0){
            log.info("账户重复");
            throw new exception(ErrorCode.PARAMS_ERROR , "账户重复");
        }

        //6.密码加密
        try {
            String newPassword = DigestUtils.md5DigestAsHex((userPassword + salt).getBytes());
            User user = new User();
            user.setUserAccount(userAccount);
            user.setPassword(newPassword);
            user.setPlanetCode(planetCode);
            user.setGender(registerRequest.getGender());
            user.setUserName(registerRequest.getUserName());
            List<String> tags = registerRequest.getTags();
            if (!CollectionUtils.isEmpty(tags)){
                Gson gson = new Gson();
                String jsonStr = gson.toJson(tags);
                user.setTags(jsonStr);
            }
            if (StringUtils.isNotBlank(registerRequest.getAvatarUrl())){
                user.setAvatarUrl(registerRequest.getAvatarUrl());
            }
            if (StringUtils.isNotBlank(registerRequest.getEmail())){
                user.setEmail(registerRequest.getEmail());
            }
            if (StringUtils.isNotBlank(registerRequest.getPhone())){
                user.setPhone(registerRequest.getPhone());
            }
            if (StringUtils.isNotBlank(registerRequest.getProfile())){
                user.setProfile(registerRequest.getProfile());
            }
            userMapper.insert(user);

            return user.getId();

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


}




